<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>大文件分片上传</title>
</head>
<body>
    <h1>大文件分片上传测试</h1>
    <input id="myFile" type="file" name="myFile"/>
    <br/>
    <div id="log"></div>
</body>

<script src="http://code.jquery.com/jquery-1.12.4.min.js"></script>
<script>
    $(function () {
        let wsServer = 'ws://127.0.0.1:9502';
        let ws = new WebSocket(wsServer);

        ws.onopen = function (evt) {
            log("Connected to WebSocket server.");
        };

        ws.onmessage = function (evt) {
            log('Retrieved data from server: ' + evt.data);
        };

        ws.onclose = function (evt) {
            log("Disconnected to WebSocket server.");
        };

        let myFile = $("#myFile");
        myFile.onchange = function () {
            let file = myFile.files[0]; // 这里可以得到上传的文件对象

            let length = 1024 * 1024 * 49; // 这里是每一个分片的大小
            let total_number = Math.ceil(file.size / length); // 使用进一法，来确定分片的个数

            // 切片的数量是由浏览器最大并发连接数决定，结合服务器上传文件大小限制综合算出
            // 计算方式：通过所上传的文件的大小，动态计算出合适的分片大小。总的来说请求次数越少，总耗时越少
            //let total_number = 6;
            //let length = Math.ceil(file.size / total_number);

            let start = 0; // 分片的初始位置
            let end = length; // 分片的结束位置
            let parr = []; // 这里为promise.all方法准备一个数组
            for (let i = 1; i <= total_number; i++) {
                // 这里开始分片，并且把每一个分片上传到服务器
                let blob = file.slice(start, end); // 得到一个分片
                start = end; // 调整下一个分片的起始位置
                end = start + length; // 调整下一个分片的结束位置
                if (end > file.size) {
                    end = file.size; // 这里对最后的一个分片结束位置进行调整
                }

                let reader = new FileReader();
                reader.readAsArrayBuffer(blob);
                reader.onload = function (e) {
                    let arrBuf = e.target.result;
                    ws.send(arrBuf);
                };
            }

            // 以上for循环结束之后，parr数组中就全部是分片上传的promise的对象了，此时我们使用promise.all方法，等待所有上传都成功执行后，再向服务器发送一个请求，也就是上传完成，让服务器组装分片的请求
            Promise.all(parr).then(res => {
                if (res.length === parr.length) { // 如果返回成功的数组长度和parr的数组长度相等，说明分片全部上传成功
                    // 此时对上传接口再次发送请求，同时把上传的文件名带上，方便后台查找要组装的分片文件名，因为是请求同一个上传接口，所以我们还要传一个flag=1表示这是一个数据组装的请求
                    let data = {flag: 1, filename: file.name};
                    $.ajax({
                        type: "post",
                        url: "http://127.0.0.1:82/admin/upload/getupload",
                        data: {flag: 1, filename: file.name}, // 这里flag=1表示上传完成，请求组装，filename表示要组成哪一组文件分片
                        success: function (res) {
                            res = $.parseJSON(res);
                            if (res.code === 200 && res.data.length === parr.length) {
                                alert('ok, 耗时：' + res.data.run);
                            }
                        },
                        fail: function () {
                            reject()
                        }
                    });
                    ws.send(data);
                }
            })
        }
    });

    // 这个函数用来上传分片文件，返回的是一个promise对象，方便后面使用promise.all还判断所有分片是否是上传成功的
    // 这里要说明一下，$.post()是不可以上传文件的，只能用$.ajax()并且要把contentType:false和processData:false带上
    function pro(formData) {
        return new Promise((resolve, reject) => {
            $.ajax({
                type: "post",
                url: "http://127.0.0.1:82/admin/upload/getupload", // 后台上传文件的地址
                data: formData,
                contentType: false, // 这个不能少，ajax上传文件是不能少的
                processData: false, // 这个不能少，ajax上传文件必传false
                success: function (res) {
                    resolve(res)
                },
                fail: function () {
                    reject()
                }
            })
        })
    }

    //在消息框中打印内容
    function log(text) {
        $("#log").append(text + "");
    }
</script>
</html>